#include "displaylist.h"

using namespace G3;

G3::DISPLAYLIST::DISPLAYLIST () {
    vertices = NULL;
    texcoords = NULL;
    normals = NULL;
    tangents = NULL;
    indexes = NULL;
    materials = NULL;

    num_vertices = 0;
    num_indexes = 0;
    num_materials = 0;

    material_applied = false;
    current_material = 0;

    log = &G3::MainLog;
}

G3::DISPLAYLIST::~DISPLAYLIST () {
    clear ();
}

//==============================================================================
// Initializes the DisplayList
//==============================================================================
void G3::DISPLAYLIST::init (uint nv, G3::VEC3F *v, uint nid, uint *id, uint nm, G3::MATERIAL *m, G3::VEC2F *uv, G3::VEC3F *n, G3::VEC3F *t) {
    num_vertices = nv;
    num_indexes = nid;
    num_materials = nm;

    vertices = v;
    texcoords = uv;
    normals = n;
    tangents = t;
    indexes = id;
    materials = m;

    // Are the pointers valid?
    if (vertices == NULL || indexes == NULL) {
        if (log != NULL)
            log->Report ("DISPLAYLIST::init: At least vertices and indexes are needed to define a buffer\n");
        return;
    }
}

//==============================================================================
// Builds a batch into a DisplayList
//==============================================================================
bool G3::DISPLAYLIST::build (BATCH *batch) {
    int i, j, id;

    if (batch != NULL) {
        if (batch->display_list_id != -1)
            glDeleteLists (batch->display_list_id, 1);

/*        glVertexPointer (3, GL_FLOAT, 0, vertices);
        glTexCoordPointer (2, GL_FLOAT, 0, texcoords);
        glNormalPointer (GL_FLOAT, 0, normals);
        glIndexPointer (GL_INT, 0, indexes);

        if (vertices != NULL)
            glEnableClientState (GL_VERTEX_ARRAY);
        if (indexes != NULL)
            glEnableClientState (GL_INDEX_ARRAY);
        if (texcoords != NULL)
            glEnableClientState (GL_TEXTURE_COORD_ARRAY);
        if (normals != NULL)
            glEnableClientState (GL_NORMAL_ARRAY);

        glDrawElements (GL_TRIANGLES, batch->numElements, GL_UNSIGNED_INT, (void *) batch->indexStart);

        glDisableClientState (GL_NORMAL_ARRAY);
        glDisableClientState (GL_TEXTURE_COORD_ARRAY);
        glDisableClientState (GL_INDEX_ARRAY);
        glDisableClientState (GL_VERTEX_ARRAY);*/

        if (indexes == NULL || vertices == NULL) {
            printf ("Invalid index and vertex array pointers..\n");
            return false;
        }

        batch->display_list_id = glGenLists (1);

        glNewList (batch->display_list_id, GL_COMPILE);

        glBegin (GL_TRIANGLES);
        for (i=0; i<(int)batch->numElements; i++) {
            id = batch->indexStart / sizeof (int) + i;

            // Abort on invalid indexes
            if (id >= num_indexes) {
                printf ("Invalid indexes, aborting..\n");
                break;
            } else
                id = indexes [id];

            if (texcoords)
                glTexCoord2f (texcoords [id].x, texcoords [id].y);
            if (normals)
                glNormal3f (normals [id].x, normals [id].y, normals [id].z);
            if (tangents)
                glTangent3fEXT (tangents [id].x, tangents [id].y, tangents [id].z);

            glVertex3f (vertices [id].x, vertices [id].y, vertices [id].z);
        }
        glEnd ();

        glEndList ();

        return true;
    }
    printf ("Trying to build a DisplayList of a NULL batch pointer..\n");

    return false;
}

//==============================================================================
// Binds a material
//==============================================================================
bool G3::DISPLAYLIST::bind_material (int mat_id) {
    if (mat_id < 0) {
        material_applied = false;
        return true;
    }

    if (materials == NULL) {
        if (log)
            log->Report ("DISPLAYLIST::bind_material: There are no materials in this DISPLAYLIST\n");
        return false;
    }
    if (mat_id >= num_materials) {
        if (log)
            log->Report ("DISPLAYLIST::bind_material: There are only %d materials in this DISPLAYLIST. However, material nr. %d was asked\n", num_materials, mat_id);
        return false;
    }

    material_applied = true;
    current_material = mat_id;

    materials [mat_id].glApply ();

    return true;
}

//==============================================================================
// Unbinds the last bound material
//==============================================================================
bool G3::DISPLAYLIST::unbind_material () {
    if (!material_applied)
        return false;

    Assert (materials, "Impossible but true: There are no materials, but one of them has been bound");
    Assert (current_material < num_materials, "Impossible but true: A nonexistent material has been bound");

    materials [current_material].glUnApply ();

    material_applied = false;
    current_material = 0;

    return true;
}

void G3::DISPLAYLIST::clear () {
    //! \todo Maybe we should store the DisplayList ID's and destroy them here instead of the batch destructor
}
